﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Xml;
using System.Windows.Input;
using System;
using SHomeWorkshop.LunarConcept.Controls;
using SHomeWorkshop.LunarConcept.Adorners;
using SHomeWorkshop.LunarConcept.Tools;
using SHomeWorkshop.LunarConcept.Enums;
using SHomeWorkshop.LunarConcept.Widgets.Interfaces;
using SHomeWorkshop.LunarMind;

namespace SHomeWorkshop.LunarConcept.Widgets
{
    /// <summary>
    /// 创建时间：2012年1月17日
    /// 创建者：  杨震宇
    /// 
    /// 主要用途：（贝塞尔）曲线部件。
    /// </summary>
    public class BezierLineWidget : ArrowLineWidget, Interfaces.ICanSameSize, Interfaces.ILinkableLine, Interfaces.ITextRotate
    {
        #region 构造方法=====================================================================================================

        /// <summary>
        /// [静态构造方法]
        /// </summary>
        static BezierLineWidget()
        {
            dashArray = new DoubleCollection() { 2, 2 };
        }

        /// <summary>
        /// [构造方法]
        /// </summary>
        public BezierLineWidget(PageEditorReader masterEditor)
            : base(masterEditor)
        {
            widgetType = Enums.WidgetTypes.BezierLine;
            widgetClassLocalName = Widget.GetWidgetClassLocalName(this.GetType().Name);

            mainPath.Data = new PathGeometry();
            mainPathFigure = new PathFigure() { IsClosed = false };
            (mainPath.Data as PathGeometry).Figures.Add(mainPathFigure);
            mainPathFigure.Segments.Add(mainBezierSegment);

            mainPath.Fill = null;
            mainPath.Stroke = WidgetForeColor;
            
            this.mainCanvas.Children.Add(mainPath);

            Canvas.SetZIndex(mainPath, 0);

            #region 两个箭头。
            //startArrowPath.Cursor = Cursors.Arrow;
            startArrowPath.Fill = startArrowPath.Stroke = WidgetForeColor;
            startArrowPath.StrokeThickness = widgetLineWidth;
            PathGeometry startArrowPg = new PathGeometry();
            startArrowPath.Data = startArrowPg;

            startArrowPathFigure.IsClosed = true;
            startArrowPathFigure.IsFilled = true;
            startArrowPathFigure.Segments.Add(startArrowPolyLineSegment);

            startArrowPg.Figures.Add(startArrowPathFigure);

            //endArrowPath.Cursor = Cursors.Arrow;
            endArrowPath.Fill = endArrowPath.Stroke = WidgetForeColor;
            endArrowPath.StrokeThickness = widgetLineWidth;
            PathGeometry endArrowPg = new PathGeometry();
            endArrowPath.Data = endArrowPg;

            endArrowPathFigure.IsClosed = true;
            endArrowPathFigure.IsFilled = true;
            endArrowPathFigure.Segments.Add(endArrowPolyLineSegment);

            endArrowPg.Figures.Add(endArrowPathFigure);

            mainCanvas.Children.Add(startArrowPath);
            mainCanvas.Children.Add(endArrowPath);

            Canvas.SetZIndex(startArrowPath, 1);
            Canvas.SetZIndex(endArrowPath, 1);
            #endregion
            
            this.commentAdorner = new CommentAdorner(this.mainPath, this) { Visibility = System.Windows.Visibility.Collapsed };//默认不显示。

            this.hyperLinkAdorner = new HyperLinkAdorner(this.mainPath, this) { Visibility = System.Windows.Visibility.Collapsed };
            this.hyperLinkAdorner.MouseLeftButtonUp += new MouseButtonEventHandler(hyperLinkAdorner_MouseLeftButtonUp);

            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.mainCanvas);
            if (adornerLayer == null)
            {
                MessageBox.Show("　　未找到Widget的装饰层！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            else
            {
                adornerLayer.Add(this.commentAdorner);//这个要在各具体部件类中添加。
                adornerLayer.Add(this.hyperLinkAdorner);//这个要在各具体部件类中添加。
            }
            
            //最后添加文本面板。//已移动到基类中。
            //this.mainCanvas.Children.Add(this.mainTextPanel);
            //Canvas.SetZIndex(this.mainTextPanel, 2);
        }

        #endregion


        #region 字段与属性===================================================================================================
        
        private Point startPoint = new Point(0, 0);
        /// <summary>
        /// [读写]线的起点坐标。
        /// </summary>
        [Tools.LunarProperty("StartPoint", PropertyDateType.Point)]
        public Point StartPoint
        {
            get { return startPoint; }
            set
            {
                startPoint = FormatPoint(value);

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.StartPointTag, startPoint.ToString());
                }

                RefreshLocation();
            }
        }

        private Point endPoint = new Point();
        /// <summary>
        /// [读写]线的终点坐标。
        /// </summary>
        [Tools.LunarProperty("EndPoint", PropertyDateType.Point)]
        public Point EndPoint
        {
            get { return endPoint; }
            set
            {
                endPoint = FormatPoint(value);

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.EndPointTag, endPoint.ToString());
                }

                RefreshLocation();
            }
        }

        private string startMasterId = string.Empty;
        /// <summary>
        /// [读写]首端点挂接的部件的Id。
        /// </summary>
        [Tools.LunarProperty("StartMasterId", PropertyDateType.String)]
        public string StartMasterId
        {
            get { return startMasterId; }
            set
            {
                startMasterId = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.StartMasterIdTag, value);
                }
            }
        }

        private string endMasterId = string.Empty;
        /// <summary>
        /// [读写]尾端点挂接到的部件的Id。
        /// </summary>
        [Tools.LunarProperty("EndMasterId", PropertyDateType.String)]
        public string EndMasterId
        {
            get { return endMasterId; }
            set
            {
                endMasterId = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.EndMasterIdTag, value);
                }
            }
        }

        private ControlDraggingType draggingType = ControlDraggingType.None;
        /// <summary>
        /// [只读]正在拖动哪个控制点。
        /// </summary>
        public ControlDraggingType DraggingType
        {
            get { return draggingType; }
        }

        /// <summary>
        /// [只读]是否被挂接到两个ContentWidget。
        /// </summary>
        public bool IsLinked
        {
            get
            {
                if (startMasterId == string.Empty && endMasterId == string.Empty)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }

        /// <summary>
        /// 连接线永不被设置为锁定。
        /// </summary>
        [Tools.LunarProperty("IsLocked", PropertyDateType.Bool)]
        public override bool IsLocked
        {
            get
            {
                if (IsLinked) return false;

                return base.IsLocked;
            }
            set
            {
                if (IsLinked) return;//连接线不被设置为锁定。

                base.IsLocked = value;
            }
        }

        private BezierLineForms lineForm = BezierLineForms.BezierLine;
        /// <summary>
        /// [读写]画成思维导图线。此属性总是可以设置的，
        /// 但必须在作为连接线时才起作用（以IsLinked为前提）。
        /// </summary>
        [Tools.LunarProperty("LineForm", PropertyDateType.BezierLineForms)]
        public BezierLineForms LineForm
        {
            get { return lineForm; }
            set
            {
                lineForm = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.LineFormTag, value.ToString());
                }

                this.RefreshLineForm();
            }
        }
        
        private Path mainPath = new Path() { Cursor = Cursors.Hand };

        public Path MainPath
        {
            get { return mainPath; }
        }

        private PathFigure mainPathFigure;

        public PathFigure MainPathFigure
        {
            get { return mainPathFigure; }
        }

        private BezierSegment mainBezierSegment = new BezierSegment();

        private BezierSegment mindBezierSegement = null;//只有作导图线时才应实例化。
        
        private Point startCPPoint = new Point();
        [Tools.LunarProperty("StartCPPoint", PropertyDateType.Point)]
        public Point StartCPPoint
        {
            get { return startCPPoint; }
            set
            {
                startCPPoint = FormatPoint(value);

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.StartCPPointTag, startCPPoint.ToString());
                }

                RefreshLocation();
            }
        }

        private Point endCPPoint = new Point();
        [Tools.LunarProperty("EndCPPoint", PropertyDateType.Point)]
        public Point EndCPPoint
        {
            get { return endCPPoint; }
            set
            {
                endCPPoint = FormatPoint(value);

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.EndCPPointTag, endCPPoint.ToString());
                }

                RefreshLocation();
            }
        }


        public override Point BottomRight
        {
            get
            {
                double minLeft, minTop, maxRight, maxBottom;

                minLeft = Math.Min(Math.Min(Math.Min(startPoint.X, endPoint.X), startCPPoint.X), endCPPoint.X);
                maxRight = Math.Max(Math.Max(Math.Max(startPoint.X, endPoint.X), startCPPoint.X), endCPPoint.X);

                minTop = Math.Min(Math.Min(Math.Min(startPoint.Y, endPoint.Y), startCPPoint.Y), endCPPoint.Y);
                maxBottom = Math.Max(Math.Max(Math.Max(startPoint.Y, endPoint.Y), startCPPoint.Y), endCPPoint.Y);

                return new Point(maxRight, maxBottom);
            }
        }

        private double textRotateAngle = 0;
        /// <summary>
        /// [读写]文本旋转角度。取值范围：[-180,180]。
        /// </summary>
        [Tools.LunarProperty("TextRotateAngle", PropertyDateType.Double)]
        public double TextRotateAngle
        {
            get { return textRotateAngle; }
            set
            {
                if (value > 180)
                {
                    textRotateAngle = 180;
                }
                else if (value < -180)
                {
                    textRotateAngle = -180;
                }
                else
                {
                    textRotateAngle = value;
                }

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.TextRotateAngleTag, textRotateAngle.ToString());
                }

                this.RefreshTextRotateAngle();
            }
        }

        public override Point TopLeft
        {
            get
            {
                double minLeft, minTop, maxRight, maxBottom;

                minLeft = Math.Min(Math.Min(Math.Min(startPoint.X, endPoint.X), startCPPoint.X), endCPPoint.X);
                maxRight = Math.Max(Math.Max(Math.Max(startPoint.X, endPoint.X), startCPPoint.X), endCPPoint.X);

                minTop = Math.Min(Math.Min(Math.Min(startPoint.Y, endPoint.Y), startCPPoint.Y), endCPPoint.Y);
                maxBottom = Math.Max(Math.Max(Math.Max(startPoint.Y, endPoint.Y), startCPPoint.Y), endCPPoint.Y);

                return new Point(minLeft, minTop);
            }
        }

        #endregion


        #region 方法=========================================================================================================

        /// <summary>
        /// 根据XmlData内容生成部件内容，设置部件格式。
        /// </summary>
        public override void Build()
        {
            base.Build();

            if (this.xmlData == null) return;

            XmlAttribute attrStartMasterId = this.xmlData.GetAttribute(XmlTags.StartMasterIdTag);
            if (attrStartMasterId != null)
            {
                this.startMasterId = attrStartMasterId.Value;
            }

            XmlAttribute attrEndMasterId = this.xmlData.GetAttribute(XmlTags.EndMasterIdTag);
            if (attrEndMasterId != null)
            {
                this.endMasterId = attrEndMasterId.Value;
            }

            XmlAttribute attrStartPoint = this.xmlData.GetAttribute(XmlTags.StartPointTag);
            if (attrStartPoint != null)
            {
                this.startPoint = FormatPoint(Point.Parse(attrStartPoint.Value));
            }

            XmlAttribute attrStartCPPoint = this.xmlData.GetAttribute(XmlTags.StartCPPointTag);
            if (attrStartCPPoint != null)
            {
                this.startCPPoint = FormatPoint(Point.Parse(attrStartCPPoint.Value));
            }

            XmlAttribute attrEndPoint = this.xmlData.GetAttribute(XmlTags.EndPointTag);
            if (attrEndPoint != null)
            {
                this.endPoint = FormatPoint(Point.Parse(attrEndPoint.Value));
            }

            XmlAttribute attrEndCPPoint = this.xmlData.GetAttribute(XmlTags.EndCPPointTag);
            if (attrEndCPPoint != null)
            {
                this.endCPPoint = FormatPoint(Point.Parse(attrEndCPPoint.Value));
            }

            XmlAttribute attrTextRotateAngle = this.xmlData.GetAttribute(XmlTags.TextRotateAngleTag);
            if (attrTextRotateAngle != null)
            {
                this.textRotateAngle = double.Parse(attrTextRotateAngle.Value);
            }

            XmlAttribute attrLineForm = this.xmlData.GetAttribute(XmlTags.LineFormTag);
            if (attrLineForm != null)
            {
                this.lineForm = (Enums.BezierLineForms)Enum.Parse(typeof(Enums.BezierLineForms), attrLineForm.Value);
            }
            else
            {
                //保持兼容，过去使用这个bool属性。2012年8月12日
                //新枚举中已经考虑到了兼容。
                XmlAttribute attrIsMindMapLine = this.xmlData.GetAttribute(XmlTags.IsMindMapLineTag);
                if (attrIsMindMapLine != null)
                {
                    this.lineForm = (Enums.BezierLineForms)Enum.Parse(typeof(Enums.BezierLineForms), attrIsMindMapLine.Value);
                }
            }

            this.RefreshTextRotateAngle();
            //包含RefreshArrows()调用。
            this.RefreshLocation();

            //此类是下面这几个属性的“最终实现类”。这些属性的值都已在基类确定。因此调用,
            this.RefreshWidgetLineColor();
            this.RefreshWidgetLineWidth();
            this.RefreshLineDash();

        }
        
        /// <summary>
        /// 根据“IsLinked”的结果分别重绘线条。
        /// </summary>
        public void DrawLine()
        {
            if (masterEditor == null) return;
            if (IsLinked)
            {
                Widget startWidget = masterEditor.GetWidget(this.StartMasterId);
                Widget endWidget = masterEditor.GetWidget(this.EndMasterId);
                if (startWidget == null || endWidget == null)
                {
                    DrawLine(this.startPoint, this.startCPPoint, this.endCPPoint, this.endPoint);
                    return;
                }

                Rect startRect = startWidget.OuterRect;
                Rect endRect = endWidget.OuterRect;
                DrawLine(startRect, this.startCPPoint, this.endCPPoint, endRect);
            }
            else
            {
                DrawLine(this.startPoint, this.startCPPoint, this.endCPPoint, this.endPoint);
            }

            this.RefreshTextPanelLocatin();
        }

        /// <summary>
        /// “IsLinked”为false时，表示此线未连接到ICanLinkedWidget部件。此时由四个点决定线的位置与形状。
        /// </summary>
        /// <param name="startPoint">首端点。</param>
        /// <param name="startCPPoint">首控制点。</param>
        /// <param name="endCPPoint">尾控制点。</param>
        /// <param name="endPoint">尾端点。</param>
        private void DrawLine(Point startPoint, Point startCPPoint, Point endCPPoint, Point endPoint)
        {
            if (this.mindBezierSegement != null &&
                this.mainPathFigure.Segments.Contains(this.mindBezierSegement))
            {
                this.mainPathFigure.Segments.Remove(this.mindBezierSegement);
            }

            this.mainPath.Visibility = Visibility.Visible;
            this.mainPathFigure.IsFilled = this.mainPathFigure.IsClosed = false;

            if (arrows == ArrowType.None)
            {
                mainPathFigure.StartPoint = startPoint;
                mainBezierSegment.Point1 = startCPPoint;
                mainBezierSegment.Point2 = endCPPoint;
                mainBezierSegment.Point3 = endPoint;
            }
            else
            {
                if (arrows == ArrowType.All || arrows == ArrowType.Start)
                {
                    startArrowPathFigure.StartPoint = startPoint;
                    Tools.ArrowPoints apStart = new Tools.ArrowPoints(
                        startArrowPathFigure.StartPoint, startCPPoint, widgetLineWidth * 2);

                    startArrowPolyLineSegment.Points = new PointCollection(){
                    apStart.ArrowLeftPoint,apStart.ArrowRightPoint,
                };
                    mainPathFigure.StartPoint = apStart.ArrowRealTopPoint;
                }
                else
                {
                    mainPathFigure.StartPoint = startPoint;
                }

                mainBezierSegment.Point1 = startCPPoint;
                mainBezierSegment.Point2 = endCPPoint;

                if (arrows == ArrowType.All || arrows == ArrowType.End)
                {
                    endArrowPathFigure.StartPoint = endPoint;
                    Tools.ArrowPoints apEnd = new Tools.ArrowPoints(
                        endArrowPathFigure.StartPoint, endCPPoint, widgetLineWidth * 2);

                    endArrowPolyLineSegment.Points = new PointCollection(){
                    apEnd.ArrowLeftPoint,apEnd.ArrowRightPoint,
                };
                    mainBezierSegment.Point3 = apEnd.ArrowRealTopPoint;
                }
                else
                {
                    mainBezierSegment.Point3 = endPoint;
                }
            }
        }

        /// <summary>
        /// ★此方法不会调用DrawLine(Point,Point,Point,Point)方法来进行绘制（会重复计算坐标点，效率较低）。
        /// ★应保持此方法与重载版本行为一致！！！
        /// 
        /// “IsLinked”属性为true时，曲线被连接到两个ICanLinkedWidget部件。
        /// 此时，曲线的位置和形状由这两个部件的外框和曲线的两个控制点决定。
        /// 曲线的首端点、尾端点将自动计算，计算的规则是：“首控制点到首矩形的中心点与矩形的交战为首端点”。
        /// </summary>
        /// <param name="startRect">首连接部件外框。</param>
        /// <param name="startCPPoint">首控制点。</param>
        /// <param name="endCPPoint">尾控制点。</param>
        /// <param name="endRect">尾连接部件外框。</param>
        private void DrawLine(Rect startRect, Point startCPPoint, Point endCPPoint, Rect endRect)
        {
            startRect.X -= 4; startRect.Y -= 4; startRect.Height += 8; startRect.Width += 8;
            endRect.X -= 4; endRect.Y -= 4; endRect.Height += 8; endRect.Width += 8;

            if (startRect.IntersectsWith(endRect)) return;

            //注意：未连接的曲线，总是不显示为导图线
            if (this.IsLinked == false || this.LineForm == BezierLineForms.BezierLine)
            {
                if (this.mindBezierSegement != null &&
                    this.mainPathFigure.Segments.Contains(this.mindBezierSegement))
                {
                    this.mainPathFigure.Segments.Remove(this.mindBezierSegement);
                }

                mainPath.Visibility = Visibility.Visible;
                this.mainPathFigure.IsFilled = this.mainPathFigure.IsClosed = false;

                this.RefreshArrows(false);

                //边缘留点空白区。
                startRect.X -= 4; startRect.Y -= 4; startRect.Width += 8; startRect.Height += 8;
                endRect.X -= 4; endRect.Y -= 4; endRect.Width += 8; endRect.Height += 8;

                PointToRect.ArrowPoints aptStart = PointToRect.GetCrossPointToRect(startRect, startCPPoint);
                startArrowPathFigure.StartPoint = aptStart.Top;
                Tools.ArrowPoints apStart = new Tools.ArrowPoints(
                    aptStart.Top, startCPPoint, widgetLineWidth * 2);

                startArrowPolyLineSegment.Points = new PointCollection()
                {
                    apStart.ArrowLeftPoint,apStart.ArrowRightPoint,
                };

                mainPathFigure.StartPoint = apStart.ArrowRealTopPoint;

                mainBezierSegment.Point1 = startCPPoint;
                mainBezierSegment.Point2 = endCPPoint;

                PointToRect.ArrowPoints aptEnd = PointToRect.GetCrossPointToRect(endRect, endCPPoint);
                endArrowPathFigure.StartPoint = aptEnd.Top;
                Tools.ArrowPoints apEnd = new Tools.ArrowPoints(
                    aptEnd.Top, endCPPoint, widgetLineWidth * 2);
                endArrowPolyLineSegment.Points = new PointCollection()
                {
                    apEnd.ArrowLeftPoint,apEnd.ArrowRightPoint,
                };
                mainBezierSegment.Point3 = apEnd.ArrowRealTopPoint;
            }
            else
            {
                //作思维导图线时总是不显示箭头。
                endArrowPath.Visibility =
                    startArrowPath.Visibility = Visibility.Collapsed;

                if (startRect.IntersectsWith(endRect))
                {
                    this.mainPath.Visibility = Visibility.Collapsed;
                    return;
                }
                else
                {
                    this.mainPath.Visibility = Visibility.Visible;
                }

                if (this.mindBezierSegement == null)
                {
                    this.mindBezierSegement = new BezierSegment();
                }

                if (this.mainPathFigure.Segments.Contains(this.mindBezierSegement) == false)
                {
                    this.mainPathFigure.Segments.Add(this.mindBezierSegement);//作导图线时，由两个曲线构成。
                }

                //基本思路：
                //根据EndRect.LeftCenter/EndRect.RightCenter/
                //EndRect.TopCenter/EndRect.BottomCenter距离startCenter的长度来决定怎样画法。

                Point tmpStartPoint, tmpStartCPPoint, tmpEndCPPoint, tmpEndPoint;
                ArrowPoints ap = GetMindLinePoints(ref startRect, ref endRect,
                    out tmpStartPoint, out tmpStartCPPoint,
                    out tmpEndCPPoint, out tmpEndPoint);

                this.mainPathFigure.StartPoint = ap.ArrowLeftPoint;
                this.mainBezierSegment.Point1 = tmpStartCPPoint;
                this.mainBezierSegment.Point2 = tmpEndCPPoint;
                this.mainBezierSegment.Point3 = tmpEndPoint;
                this.mindBezierSegement.Point1 = tmpEndCPPoint;
                this.mindBezierSegement.Point2 = tmpStartCPPoint;
                this.mindBezierSegement.Point3 = ap.ArrowRightPoint;

                //刷新文本块位置时要用
                this.startCPPoint = tmpStartCPPoint;
                this.endCPPoint = tmpEndCPPoint;

                this.mainPathFigure.IsFilled = this.mainPathFigure.IsClosed = true;
                this.mainPath.StrokeLineJoin = PenLineJoin.Bevel;
                this.mainPath.Stroke = this.mainPath.Fill = this.widgetLineColor;
            }
        }

        public ArrowPoints GetMindLinePoints(ref Rect startRect, ref Rect endRect,
            out Point tmpStartPoint, out Point tmpStartCPPoint,
            out Point tmpEndCPPoint, out Point tmpEndPoint)
        {
            Point startCenter, endCenter;
            startCenter = new Point(startRect.Left + startRect.Width / 2, startRect.Top + startRect.Height / 2);
            endCenter = new Point(endRect.Left + endRect.Width / 2, endRect.Top + endRect.Height / 2);

            Point endRectLeftCenter = new Point(endRect.Left, endCenter.Y);
            Point endRectRightCenter = new Point(endRect.Right, endCenter.Y);
            Point endRectTopCenter = new Point(endCenter.X, endRect.Top);
            Point endRectBottomCenter = new Point(endCenter.X, endRect.Bottom);

            double lxoffset = Math.Abs(startCenter.X - endRectLeftCenter.X);
            double lyoffset = Math.Abs(startCenter.Y - endRectLeftCenter.Y);
            double leftToStartCenter = Math.Sqrt(lxoffset * lxoffset + lyoffset * lyoffset);

            double rxoffset = Math.Abs(startCenter.X - endRectRightCenter.X);
            double ryoffset = Math.Abs(startCenter.Y - endRectRightCenter.Y);
            double rightToStartCenter = Math.Sqrt(rxoffset * rxoffset + ryoffset * ryoffset);

            double txoffset = Math.Abs(startCenter.X - endRectTopCenter.X);
            double tyoffset = Math.Abs(startCenter.Y - endRectTopCenter.Y);
            double topToStartCenter = Math.Sqrt(txoffset * txoffset + tyoffset * tyoffset);

            double bxoffset = Math.Abs(startCenter.X - endRectBottomCenter.X);
            double byoffset = Math.Abs(startCenter.Y - endRectBottomCenter.Y);
            double bottomToStartCenter = Math.Sqrt(bxoffset * bxoffset + byoffset * byoffset);

            //优先左右。实在没办法才算上下。

            if (endRect.Left > startRect.Right ||       //只要在右侧，不计算距离。
                IsMinLength(leftToStartCenter, rightToStartCenter, topToStartCenter, bottomToStartCenter))
            {
                //在右侧，画向左边中点。

                PointToRect.ArrowPoints ap = Tools.PointToRect.GetCrossPointToRect(startRect, endRectLeftCenter);
                tmpStartCPPoint = tmpEndCPPoint =
                    new Point(endRectLeftCenter.X - Math.Abs(ap.Top.X - endRectLeftCenter.X) / 2, endRectLeftCenter.Y);
                ArrowPoints apStart = ArrowPoints.GetArrowPoints(ap.Top, tmpStartCPPoint, widgetLineWidth);
                tmpStartPoint = apStart.ArrowRealTopPoint;
                tmpEndPoint = endRectLeftCenter;
                return apStart;
            }

            if (endRect.Right < startRect.Left ||       //只要在左侧，不计算距离。
                IsMinLength(rightToStartCenter, leftToStartCenter, topToStartCenter, bottomToStartCenter))
            {
                //在左侧，画向右边中点。

                PointToRect.ArrowPoints ap = Tools.PointToRect.GetCrossPointToRect(startRect, endRectRightCenter);
                tmpStartCPPoint = tmpEndCPPoint =
                    new Point(endRectRightCenter.X + Math.Abs(ap.Top.X - endRectRightCenter.X) / 2, endRectRightCenter.Y);
                ArrowPoints apStart = ArrowPoints.GetArrowPoints(ap.Top, tmpStartCPPoint, widgetLineWidth);
                tmpStartPoint = apStart.ArrowRealTopPoint;
                tmpEndPoint = endRectRightCenter;
                return apStart;
            }

            if (IsMinLength(topToStartCenter, leftToStartCenter, rightToStartCenter, bottomToStartCenter))
            {
                //在下侧，画向顶边中点。

                PointToRect.ArrowPoints ap = Tools.PointToRect.GetCrossPointToRect(startRect, endRectTopCenter);
                tmpStartCPPoint = new Point(ap.Top.X, startRect.Bottom + Math.Abs(endRect.Top - startRect.Bottom) / 3);
                tmpEndCPPoint = new Point(ap.Top.X, endRect.Top - Math.Abs(endRect.Top - startRect.Bottom) / 3);
                ArrowPoints apStart = ArrowPoints.GetArrowPoints(ap.Top, tmpStartCPPoint, widgetLineWidth);
                tmpStartPoint = apStart.ArrowRealTopPoint;
                tmpEndPoint = endRectTopCenter;
                return apStart;
            }

            if (IsMinLength(bottomToStartCenter, topToStartCenter, leftToStartCenter, rightToStartCenter))
            {
                //在上侧，画向底边中点。

                PointToRect.ArrowPoints ap = Tools.PointToRect.GetCrossPointToRect(startRect, endRectBottomCenter);
                tmpStartCPPoint = new Point(ap.Top.X, startRect.Top - Math.Abs(endRect.Bottom - startRect.Top) / 3);
                tmpEndCPPoint = new Point(ap.Top.X, endRect.Bottom + Math.Abs(endRect.Bottom - startRect.Top) / 3);
                ArrowPoints apStart = ArrowPoints.GetArrowPoints(ap.Top, tmpStartCPPoint, widgetLineWidth);
                tmpStartPoint = apStart.ArrowRealTopPoint;
                tmpEndPoint = endRectBottomCenter;
                return apStart;
            }

            tmpStartPoint = tmpStartCPPoint = tmpEndCPPoint = tmpEndPoint = new Point(0, 0);//其实没用处。

            return null;
        }

        /// <summary>
        /// 判断length是否比其它几个参数指定的值都小。
        /// </summary>
        /// <returns></returns>
        private bool IsMinLength(double length, double lengthA, double lengthB, double lengthC)
        {
            if (lengthA < length) return false;
            if (lengthB < length) return false;
            if (lengthC < length) return false;

            return true;
        }
        
        /// <summary>
        /// 重定文本位置。
        /// </summary>
        /// <param name="startPoint">首端点。</param>
        /// <param name="startCPPoint">首控制点。</param>
        /// <param name="endCPPoint">尾控制点。</param>
        /// <param name="endPoint">尾端点。</param>
        private void LocateTextPanel(Point startPoint, Point startCPPoint, Point endCPPoint, Point endPoint)
        {
            switch (this.lineForm)
            {
                case BezierLineForms.MindMapLinkLine:
                    {
                        Canvas.SetLeft(this.mainBorder, this.startCPPoint.X - this.mainBorder.ActualWidth / 2);
                        Canvas.SetTop(this.mainBorder, this.startCPPoint.Y - this.mainBorder.ActualHeight / 2);
                        break;
                    }
                default:
                    {
                        double t = 0.5;

                        //Math.Pow(1 - t, 3) * P0.X + 3 * P1.X * t * Math.Pow(1 - t, 2) + 3 * P2.X * Math.Pow(t, 2) * (1 - t) + P3.X * Math.Pow(0.5, 3)

                        Point center = new Point(
                            Math.Pow(1 - t, 3) * startPoint.X +
                            3 * startCPPoint.X * t * Math.Pow(1 - t, 2) +
                            3 * endCPPoint.X * Math.Pow(t, 2) * (1 - t) + endPoint.X * Math.Pow(0.5, 3),
                            Math.Pow(1 - t, 3) * startPoint.Y +
                            3 * startCPPoint.Y * t * Math.Pow(1 - t, 2) +
                            3 * endCPPoint.Y * Math.Pow(t, 2) * (1 - t) + endPoint.Y * Math.Pow(0.5, 3));

                        Point textStart = new Point(center.X - this.mainBorder.ActualWidth / 2 - this.mainBorder.Margin.Left,
                            center.Y - this.mainBorder.ActualHeight / 2 - this.mainBorder.Margin.Top);

                        Canvas.SetLeft(this.mainBorder, textStart.X);
                        Canvas.SetTop(this.mainBorder, textStart.Y);
                        break;
                    }
            }
        }

        private void LocateTextPanel(Rect startRect, Point startCPPoint, Point endCPPoint, Rect endRect)
        {
            Point startCenter = new Point(startRect.Left + startRect.Width / 2,
                startRect.Top + startRect.Height / 2);
            Point endCenter = new Point(endRect.Left + endRect.Width / 2,
                endRect.Top + endRect.Height / 2);

            Tools.PointToRect.ArrowPoints aptStart = Tools.PointToRect.GetCrossPointToRect(startRect, startCPPoint);
            Tools.PointToRect.ArrowPoints aptEnd = Tools.PointToRect.GetCrossPointToRect(endRect, endCPPoint);

            Point startPoint = aptStart.Top;
            Point endPoint = aptEnd.Top;

            LocateTextPanel(startPoint, startCPPoint, endCPPoint, endPoint);//都不是成员字段。
        }
        
        private Point FormatStartMovingPoint(Point newInsideMovingPoint, Point endPoint)
        {
            double horizontalDistance = newInsideMovingPoint.X - endPoint.X;
            double verticalDistance = newInsideMovingPoint.Y - endPoint.Y;

            double absHD = Math.Abs(horizontalDistance);
            double absVD = Math.Abs(verticalDistance);

            if (absVD > absHD)
            {
                if (absVD > absHD * 2)
                {
                    newInsideMovingPoint = new Point(endPoint.X, newInsideMovingPoint.Y);
                }
                else
                {
                    int qua = Getquadrant(newInsideMovingPoint, endPoint);
                    switch (qua)
                    {
                        case 1:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 2:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 3:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 4:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                    endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                    }
                }
            }
            else
            {
                if (absVD * 2 < absHD)
                {
                    newInsideMovingPoint = new Point(newInsideMovingPoint.X, endPoint.Y);
                }
                else
                {
                    int qua = Getquadrant(newInsideMovingPoint, endPoint);
                    switch (qua)
                    {
                        case 1:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 2:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 3:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 4:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                    endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                    }
                }
            }

            return newInsideMovingPoint;
        }

        /// <summary>
        /// 以basePoint为基础坐标，画出四象限。
        /// 取出pt在哪个象限中。
        /// </summary>
        /// <param name="pt"></param>
        /// <param name="basePoint"></param>
        /// <returns></returns>
        private int Getquadrant(Point pt, Point basePoint)
        {
            if (pt.X > basePoint.X)
            {
                if (pt.Y > basePoint.Y)
                {
                    return 4;
                }
                else
                {
                    return 1;
                }
            }
            else
            {
                if (pt.Y > basePoint.Y)
                {
                    return 3;
                }
                else
                {
                    return 2;
                }
            }
        }

        public override string GetRelativeOuterXml(Point baseCopyTopLeft)
        {
            if (this.xmlData == null) return string.Empty;

            Point oldStartPoint = startPoint;
            Point newStartPoint = new Point(oldStartPoint.X - baseCopyTopLeft.X,
                oldStartPoint.Y - baseCopyTopLeft.Y);
            this.xmlData.SetAttribute(XmlTags.StartPointTag, newStartPoint.ToString());

            Point oldStartCPPoint = startCPPoint;
            Point newStartCPPoint = new Point(oldStartCPPoint.X - baseCopyTopLeft.X,
                oldStartCPPoint.Y - baseCopyTopLeft.Y);
            this.xmlData.SetAttribute(XmlTags.StartCPPointTag, newStartCPPoint.ToString());

            Point oldEndPoint = endPoint;
            Point newEndPoint = new Point(oldEndPoint.X - baseCopyTopLeft.X,
                oldEndPoint.Y - baseCopyTopLeft.Y);
            this.xmlData.SetAttribute(XmlTags.EndPointTag, newEndPoint.ToString());

            Point oldEndCPPoint = endCPPoint;
            Point newEndCPPoint = new Point(oldEndCPPoint.X - baseCopyTopLeft.X,
                oldEndCPPoint.Y - baseCopyTopLeft.Y);
            this.xmlData.SetAttribute(XmlTags.EndCPPointTag, newEndCPPoint.ToString());

            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            sb.Append(this.xmlData.OuterXml);

            this.xmlData.SetAttribute(XmlTags.StartPointTag, oldStartPoint.ToString());
            this.xmlData.SetAttribute(XmlTags.StartCPPointTag, oldStartCPPoint.ToString());
            this.xmlData.SetAttribute(XmlTags.EndPointTag, oldEndPoint.ToString());
            this.xmlData.SetAttribute(XmlTags.EndCPPointTag, oldEndCPPoint.ToString());
            return sb.ToString();
        }
        
        /// <summary>
        /// 刷新箭头状态。
        /// </summary>
        public override void RefreshArrows()
        {
            this.RefreshArrows(true);
        }

        /// <summary>
        /// 刷新箭头状态。
        /// </summary>
        /// <param name="drawLine">若为真，按已设置的startPoint/startCPpoint/endCPPoint/endPoint画线及箭头。</param>
        private void RefreshArrows(bool drawLine)
        {
            base.RefreshArrows();

            if (drawLine) DrawLine();

            if (this.IsLinked && this.LineForm == BezierLineForms.MindMapLinkLine)
            {
                this.endArrowPath.Visibility = this.startArrowPath.Visibility =
                    Visibility.Collapsed;//作导图线时总是隐藏箭头。
            }
        }
        
        public override void RefreshLineDash()
        {
            switch (lineDash)
            {
                case LineDashType.DashType.Dash:
                    {
                        this.mainPath.StrokeDashArray = LineDashType.dashCollection; break;
                    }
                case LineDashType.DashType.DashDotDot:
                    {
                        this.mainPath.StrokeDashArray = LineDashType.dashDotDotCollection; break;
                    }
                case LineDashType.DashType.Dot:
                    {
                        this.mainPath.StrokeDashArray = LineDashType.dotCollection; break;
                    }
                case LineDashType.DashType.Solid:
                    {
                        this.mainPath.StrokeDashArray = LineDashType.solidCollection; break;
                    }
                default:
                    {
                        this.mainPath.StrokeDashArray = LineDashType.dashDotCollection; break;
                    }
            }
        }

        public void RefreshLineForm()
        {
            this.DrawLine();
            //this.RefreshTextPanelLocatin();//this.DrawLine()末尾会自动调用。
        }

        /// <summary>
        /// 刷新线宽。
        /// </summary>
        public override void RefreshWidgetLineWidth()
        {
            base.RefreshWidgetLineWidth();

            //强制曲线跳过宽度１（当作２看待）——因为会模糊，很难看。
            //mainPath.StrokeThickness = (lineWidth < 2 ? 2 : lineWidth);

            double newLineWidth = (widgetLineWidth < 1 ? 1 : widgetLineWidth);
            mainPath.StrokeThickness = newLineWidth;//还是支持宽度1的线为妙。

            //if (widgetLineWidth == 1)
            //{
            //    RenderOptions.SetEdgeMode(mainPath, EdgeMode.Aliased);
            //}
            //else
            //{
            //    RenderOptions.SetEdgeMode(mainPath, EdgeMode.Unspecified);
            //}

            startArrowPath.StrokeThickness = endArrowPath.StrokeThickness = newLineWidth;
        }

        public override void RefreshLocation()
        {
            base.RefreshLocation();

            DrawLine();

            //mainPathFigure.StartPoint = startPoint;
            //mainBezierSegment.Point1 = startCPPoint;
            //mainBezierSegment.Point2 = endCPPoint;
            //mainBezierSegment.Point3 = endPoint;

            RefreshWidgetLineWidth();
            RefreshArrows();

            this.RefreshTextRotateAngle();
        }

        public override void RefreshPointWhenGroupIn(Point baseTopLeft)
        {
            Point oldStartPoint = startPoint;
            Point newStartPoint = new Point(oldStartPoint.X - baseTopLeft.X,
                oldStartPoint.Y - baseTopLeft.Y);
            StartPoint = newStartPoint;

            Point oldStartCPPoint = startCPPoint;
            Point newStartCPPoint = new Point(oldStartCPPoint.X - baseTopLeft.X,
                oldStartCPPoint.Y - baseTopLeft.Y);
            StartCPPoint = newStartCPPoint;

            Point oldEndPoint = endPoint;
            Point newEndPoint = new Point(oldEndPoint.X - baseTopLeft.X,
                oldEndPoint.Y - baseTopLeft.Y);
            EndPoint = newEndPoint;

            Point oldEndCPPoint = endCPPoint;
            Point newEndCPPoint = new Point(oldEndCPPoint.X - baseTopLeft.X,
                oldEndCPPoint.Y - baseTopLeft.Y);
            EndCPPoint = newEndCPPoint;
        }

        public override void RefreshPointWhenGroupOut(Point baseTopLeft)
        {
            Point oldStartPoint = startPoint;
            Point newStartPoint = new Point(oldStartPoint.X + baseTopLeft.X,
                oldStartPoint.Y + baseTopLeft.Y);
            StartPoint = newStartPoint;

            Point oldStartCPPoint = startCPPoint;
            Point newStartCPPoint = new Point(oldStartCPPoint.X + baseTopLeft.X,
                oldStartCPPoint.Y + baseTopLeft.Y);
            StartCPPoint = newStartCPPoint;

            Point oldEndPoint = endPoint;
            Point newEndPoint = new Point(oldEndPoint.X + baseTopLeft.X,
                oldEndPoint.Y + baseTopLeft.Y);
            EndPoint = newEndPoint;

            Point oldEndCPPoint = endCPPoint;
            Point newEndCPPoint = new Point(oldEndCPPoint.X + baseTopLeft.X,
                oldEndCPPoint.Y + baseTopLeft.Y);
            EndCPPoint = newEndCPPoint;
        }

        public override void RefreshWidgetLineColor()
        {
            startArrowPath.Stroke = endArrowPath.Stroke =
                startArrowPath.Fill = endArrowPath.Fill =
                    mainPath.Stroke = mainPath.Fill = WidgetLineColor;
        }

        public override void RefreshIsShadowVisible()
        {
            base.RefreshIsShadowVisible();

            if (isShadowVisible)
            {
                this.mainPath.Effect = Widget.ShadowEffect;
            }
            else
            {
                this.mainPath.Effect = null;
            }
        }

        public override void RefreshTextPanelLocatin()
        {
            LocateTextPanel(this.startPoint, this.startCPPoint, this.endCPPoint, this.endPoint);
        }

        /// <summary>
        /// 刷新文本区旋转角度。
        /// </summary>
        public void RefreshTextRotateAngle()
        {
            if (this.mainTextPanel.RenderTransformOrigin != DefaultRenderCenter)
            {
                this.mainTextPanel.RenderTransformOrigin = DefaultRenderCenter;
            }

            if (this.textRotateAngle == 0)
            {
                this.mainTextPanel.RenderTransform = DefaultRotateTransform;
            }
            else
            {
                this.mainTextPanel.RenderTransform = new RotateTransform(textRotateAngle);
            }
        }
        #endregion


        #region 其它=========================================================================================================


        //public enum ControlDraggingType { Start, End, None }
        //2012年5月30日已移植并合并至Enums.LineCtrlDraggingType文件中。

        private static DoubleCollection dashArray;

        #endregion
    }
}
